/**
 * This file is Copyright © 2008 Software Craftsmen Limited. All Rights Reserved.
 */
package com.softwarecraftsmen.functors;

import org.jetbrains.annotations.NotNull;

public abstract class UnaryReturningFunctorBoolean<A> extends UnaryReturningFunctor<A, Boolean>
{
	@NotNull
	public final UnaryReturningFunctorBoolean<A> not()
	{
		return not(this);
	}

	@NotNull
	public final UnaryReturningFunctorBoolean<A> and(final @NotNull UnaryReturningFunctor<A, Boolean>... functors)
	{
		return and(this, functors);
	}

	@NotNull
	public final UnaryReturningFunctorBoolean<A> or(final @NotNull UnaryReturningFunctor<A, Boolean>... functors)
	{
		return or(this, functors);
	}

	@NotNull
	public static <A> UnaryReturningFunctorBoolean<A> toUnaryReturningFunctorBoolean(final @NotNull UnaryReturningFunctor<A, Boolean> functor)
	{
		if (functor instanceof UnaryReturningFunctorBoolean)
		{
			return (UnaryReturningFunctorBoolean<A>) functor;
		}
		return new UnaryReturningFunctorBoolean<A>()
		{
			@NotNull
			public Boolean closure(final @NotNull A a)
			{
				return functor.closure(a);
			}
		};
	}

	@NotNull
	public static <A> UnaryReturningFunctorBoolean<A> not(final @NotNull UnaryReturningFunctor<A, Boolean> that)
	{
		return new UnaryReturningFunctorBoolean<A>()
		{
			@NotNull
			public Boolean closure(final @NotNull A a)
			{
				return that.closure(a).not();
			}
		};
	}

	@NotNull
	public static <A> UnaryReturningFunctorBoolean<A> and(final @NotNull UnaryReturningFunctor<A, Boolean> left, final @NotNull UnaryReturningFunctor<A, Boolean>... functors)
	{
		return new UnaryReturningFunctorBoolean<A>()
		{
			@NotNull
			public Boolean closure(final @NotNull A a)
			{
				final Boolean result = left.closure(a);
				if (result.isFalse())
				{
					return result;
				}
				for (UnaryReturningFunctor<A, Boolean> functor : functors)
				{
					final Boolean aBoolean = functor.closure(a);
					if (aBoolean.isFalse())
					{
						return result;
					}
				}
				return Boolean.True;
			}
		};
	}

	@NotNull
	public static <A> UnaryReturningFunctorBoolean<A> or(final @NotNull UnaryReturningFunctor<A, Boolean> left, final @NotNull UnaryReturningFunctor<A, Boolean>... functors)
	{
		return new UnaryReturningFunctorBoolean<A>()
		{
			@NotNull
			public Boolean closure(final @NotNull A a)
			{
				Boolean result = left.closure(a);
				for (UnaryReturningFunctor<A, Boolean> functor : functors)
				{
					final Boolean that = functor.closure(a);
					result = result.or(that);
				}
				return result;
			}
		};
	}
}